package com.palm.vue;

import com.inet.lib.less.Less;

import java.io.InputStream;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class VueCompressor {
    private static Pattern templatePattern = Pattern.compile("<template\\s*>", Pattern.DOTALL | Pattern.CASE_INSENSITIVE);
    private static Pattern scriptPattern = Pattern.compile("(?:<script\\s*(setup)?\\s*>|<script type=['\"].+['\"]\\s*>|<script lang=['\"].+['\"]\\s*>)(.*?)</script>", Pattern.DOTALL | Pattern.CASE_INSENSITIVE | Pattern.MULTILINE);
    private static Pattern stylePattern = Pattern.compile("<style[^>()]*?>(.+)</style>", Pattern.DOTALL | Pattern.CASE_INSENSITIVE | Pattern.MULTILINE);
    private static Pattern importPattern = Pattern.compile("import\\s+([\\{\\w\\,\\}\\s]+\\s+from\\s+)?(['\"][\\.\\-\\/\\w]+['\"])", Pattern.DOTALL);

    public VueCompressor() {

    }

    public String compile(String source, String path) {
        String script = "var _sfc={};";
        String setUpScript = null;
        String style = null;
        String template = null;
        StringBuilder append = new StringBuilder();
        Matcher templateMatcher = templatePattern.matcher(source);
        if (templateMatcher.find()) {
            int start = templateMatcher.end();
            int end = source.lastIndexOf("</template>");
            template = source.substring(start, end);
            template = toStrBlock(template);
            append.append("template:" + template);
        }
        Matcher scriptMatcher = scriptPattern.matcher(source);

        while (scriptMatcher.find()) {
            boolean setup = Objects.equals("setup", scriptMatcher.group(1));

            Set<String> vars = new HashSet<>();
            String src = scriptMatcher.group(2);
            src = src.replaceFirst("(^|\\s)export\\s+default\\s*\\{", "\nconst _sfc={");
            Matcher importMatcher = importPattern.matcher(src);
            StringBuffer sb = new StringBuffer();
            while (importMatcher.find()) {
                String g1 = importMatcher.group(1);
                String g2 = importMatcher.group(2);
                if (g1 == null) {
                    importMatcher.appendReplacement(sb, "require(" + g2 + ")");
                } else {
                    String name = g1.replaceAll("\\s+from\\s+$", "");
                    name = name.replaceAll("\\s+as\\s+", ":");
                    importMatcher.appendReplacement(sb, "const " + name + " = require(" + g2 + ")");
                    if (setup) {
                        vars.addAll(Arrays.asList(name.replaceAll("(\\w+:)|\\{|\\}", "").split(",")));
                    }
                }
            }
            importMatcher.appendTail(sb);
            if (setup) {
                String code = sb.toString();
                vars.addAll(ScriptScope.globalVars(code));
                setUpScript="Object.assign(_sfc,{setup(__props){\n"+code+"return {"+String.join(",",vars)+"}\n}})";
            } else {
                script = sb.toString();
            }

        }
        Matcher styleMatcher = stylePattern.matcher(source);
        if (styleMatcher.find()) {
            style = styleMatcher.group(1);
            style = transform(style);
            if (path != null) {
                //"\n/*# sourceURL=" + path + "*/";
                style += "\n/*# sourceURL=" + (path.replaceAll("\\.js", ".css")) + "*/";
            }
            style = toStrBlock(style);
            if (append.length() > 0) {
                append.append(",");
            }
            append.append("style:" + style);
        }
        if(setUpScript!=null){
            script+=setUpScript;
        }
        String cName=path.replaceAll("\\.js","");
        String partA = "_$(()=>{" + script + "\n_sfc.__file='"+cName+"'\nreturn _sfc\n})";

        return "define(((_)=>(require) =>_$(" + partA + ", _))({" + append + "}))";
    }

    public String compile(InputStream in) {
        String src = VueResourceLoader.loadText(in);
        return compile(src, null);
    }

    private static String toStrBlock(String source) {
        return "`" + source.replaceAll("\\\\", "\\\\").replaceAll("`", "\\`") + "`";
    }

    private static String transform(String css) {
        return Less.compile(null, css, false);
    }



}
